package com.yangyang.rpc.core.netcom;

import com.yangyang.rpc.core.netcom.common.NetComEnum;
import com.yangyang.rpc.core.netcom.common.annotation.RpcService;
import com.yangyang.rpc.core.netcom.common.bean.RpcRequest;
import com.yangyang.rpc.core.netcom.common.bean.RpcResponse;
import com.yangyang.rpc.core.netcom.common.server.AbstractServer;
import com.yangyang.rpc.core.registry.ServiceRegistry;
import com.yangyang.rpc.core.serialize.SerializeEnum;
import com.yangyang.rpc.core.serialize.Serializer;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.HashMap;
import java.util.Map;

/**
 * 服务端工厂
 * Created by chenshunyang on 2016/11/19.
 */
public class NetComServerFactory implements InitializingBean,DisposableBean,ApplicationContextAware {
    private static final Logger LOGGER = LoggerFactory.getLogger(NetComServerFactory.class);

    private static  Map<String, Object> serviceMap = new HashMap<String, Object>(); // 存放接口名与服务对象之间的映射关系

    private AbstractServer server;
    private int port = 6660;
    private NetComEnum netcom = NetComEnum.NETTY;
    private Serializer serializer = SerializeEnum.JSON.serializer;
    private String registerAddress;

    public void setSerializer(String serializer) {
        this.serializer = SerializeEnum.match(serializer, SerializeEnum.HESSIAN).serializer;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public void setNetcom(NetComEnum netcom) {
        this.netcom = netcom;
    }

    public void setRegisterAddress(String registerAddress) {
        this.registerAddress = registerAddress;
    }

    public NetComServerFactory(){

    }

    public void afterPropertiesSet() throws Exception {
        //初始化服务端
        server = netcom.serverClass.newInstance();
        //启用注册中心
        if (registerAddress != null){
            ServiceRegistry.register(port,registerAddress,serviceMap.keySet());
        }
        server.start(port,serializer);


    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        Map<String, Object> serviceBeanMap = ctx.getBeansWithAnnotation(RpcService.class); // 获取所有带有 RpcService 注解的 Spring Bean
        if (MapUtils.isNotEmpty(serviceBeanMap)) {
            for (Object serviceBean : serviceBeanMap.values()) {
                String interfaceName = serviceBean.getClass().getAnnotation(RpcService.class).value().getName();
                serviceMap.put(interfaceName, serviceBean);
                LOGGER.info("interface:["+interfaceName+"] can provide service");
            }
        }
    }

    public static RpcResponse invokeService(RpcRequest request, Object serviceBean) throws Exception {
        if (serviceBean==null) {
            serviceBean = serviceMap.get(request.getClassName());
        }
        if (serviceBean == null) {
            LOGGER.error("no serviceBean found ,please check server");
            throw new Exception("no serviceBean found ,please check server");
        }
        RpcResponse response = new RpcResponse();
        response.setRequestId(request.getRequestId());
        try {
            Object result = hander(request,serviceBean);
            LOGGER.info("requestId:{},server response result:",response.getRequestId(),result.toString());
            response.setResult(result);
        } catch (Throwable t) {
            LOGGER.info("requestId:{},server throw exception:{}",request.getRequestId(),t.getStackTrace());
            response.setError(t);
        }

        return response;

    }

    private static Object hander(RpcRequest request,Object serviceBean) throws Exception{
        Class<?> serviceClass = serviceBean.getClass();
        String methodName = request.getMethodName();
        Class<?>[] parameterTypes = request.getParameterTypes();
        Object[] parameters = request.getParameters();
        //java反射方式实现
        //		Method method = serviceClass.getMethod(methodName, parameterTypes);
        //		method.setAccessible(true);
        //		return method.invoke(serviceBean, parameters);

        //cglib反射方式实现，性能更高
        FastClass serviceFastClass = FastClass.create(serviceClass);
        FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes);
        return serviceFastMethod.invoke(serviceBean, parameters);
    }

    @Override
    public void destroy() throws Exception {
        LOGGER.debug("server has destroy");
        server.destroy();
    }
}
